package com.sad.jetpack.v1.promise;

import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Main {

    public static void main(String[] args) {
        System.out.println("====== Promise 演示开始 ======");

        demoSuccessfulChain();
        sleep(2000); // 等待上一个演示可能完成

        demoFailingChain();
        sleep(2000);

        demoPromiseAll();
        sleep(2000);

        demoPromiseRace();
        sleep(2000);

        demoChainedAsync();
        sleep(3000); // 这个演示的链条更长

        demoHandle();
        sleep(2000);

        demoTimeout();
        sleep(2000);

        demoCustomExecutor();
        sleep(2000);

        System.out.println("\n====== Promise 演示结束 ======");
    }

    /**
     * 演示一个成功的、顺序执行的 Promise 链。
     */
    private static void demoSuccessfulChain() {
        System.out.println("\n--- 演示: 成功的链式调用 ---");
        Promise.resolved("Start")
            .then("第一步: 转为大写", String::toUpperCase)
            .then("第二步: 追加 World", s -> s + " WORLD")
            .then(result -> {
                System.out.println("  最终结果: " + result);
                return null;
            })
            .except(Main::handleError)
            .lastly(() -> System.out.println("  链条已结束。"));
    }

    /**
     * 演示一个失败并被 except 块捕获的链。
     */
    private static void demoFailingChain() {
        System.out.println("\n--- 演示: 带命名步骤的失败链 ---");
        Promise.resolved("user-id-123")
            .then("获取用户配置", userId -> {
                System.out.println("  尝试获取配置，用户ID: " + userId);
                throw new IllegalStateException("数据库连接丢失");
            })
            .then(config -> {
                // 这部分不会被执行
                System.out.println("  这行代码永远不应被打印。");
                return null;
            })
            .except(Main::handleError)
            .lastly(() -> System.out.println("  链条已结束。"));
    }

    /**
     * 演示 Promise.all() 并发执行多个任务。
     */
    private static void demoPromiseAll() {
        System.out.println("\n--- 演示: Promise.all() 并发 ---");
        Promise<String> p1 = new Promise<>((res, rej) -> sleep(500, () -> res.accept("API 1 结果")));
        Promise<String> p2 = new Promise<>((res, rej) -> sleep(800, () -> res.accept("API 2 结果")));
        Promise.all(p1, p2) // 使用 varargs 版本
            .then("聚合所有结果", results -> {
                System.out.println("  所有任务完成。结果: " + results);
                return null;
            })
            .except(Main::handleError)
            .lastly(() -> System.out.println("  链条已结束。"));
    }

    /**
     * 演示 Promise.race() 获取最快的结果。
     */
    private static void demoPromiseRace() {
        System.out.println("\n--- 演示: Promise.race() 竞争最快结果 ---");
        Promise<String> fastServer = new Promise<>((res, rej) -> sleep(300, () -> res.accept("快速服务器胜出!")));
        Promise<String> slowServer = new Promise<>((res, rej) -> sleep(600, () -> res.accept("慢速服务器胜出!")));

        Promise.race(fastServer, slowServer) // 使用 varargs 版本
            .then("宣布竞争获胜者", winner -> {
                System.out.println("  竞争结束。获胜者: " + winner);
                return null;
            })
            .except(Main::handleError)
            .lastly(() -> System.out.println("  链条已结束。"));
    }

    /**
     * 演示链式异步操作，其中一个 `then` 返回一个新的 Promise。
     */
    private static void demoChainedAsync() {
        System.out.println("\n--- 演示: 链式异步操作 ---");
        Promise.resolved("user-id-456")
            .then("获取用户名", userId -> {
                System.out.println("  第一步: 得到用户ID '" + userId + "'。异步获取用户名。");
                // 这个 then 块返回一个新的 Promise，链条会等待它完成。
                return new Promise<String>((resolve, reject) -> {
                    sleep(1000, () -> resolve.accept("AsyncUser"));
                });
            })
            .then("处理用户名", username -> {
                System.out.println("  第二步: 得到用户名 '" + username + "'。正在处理...");
                return "欢迎, " + username;
            })
            .then(finalResult -> {
                System.out.println("  第三步: 最终结果是 '" + finalResult + "'.");
                return null;
            })
            .except(Main::handleError)
            .lastly(() -> System.out.println("  链条已结束。"));
    }

    /**
     * 演示 handle 方法，统一处理成功和失败的情况。
     */
    private static void demoHandle() {
        System.out.println("\n--- 演示: handle 方法 ---");
        // 成功路径
        Promise.resolved("数据")
            .handle((value, error) -> {
                if (error != null) {
                    System.out.println("  handle 中发生错误: " + error.getMessage());
                    return "默认值";
                }
                return "处理后的: " + value;
            })
            .then(result -> {
                System.out.println("  handle 后的结果: " + result);
                return null;
            });

        sleep(500);

        // 失败路径
        Promise.rejected(new RuntimeException("网络超时"))
            .handle((value, error) -> {
                if (error != null) {
                    System.err.println("  handle 中捕获到错误: " + error.getMessage());
                    return "从错误中恢复的默认值"; // 从失败中恢复
                }
                return "处理后的: " + value;
            })
            .then(result -> {
                System.out.println("  handle 后的结果: " + result);
                return null;
            });
    }

    /**
     * 演示 orTimeout 方法。
     */
    private static void demoTimeout() {
        System.out.println("\n--- 演示: orTimeout 方法 ---");
        // 这个 Promise 会超时
        new Promise<>((res, rej) -> sleep(1000, () -> res.accept("太慢了")))
            .orTimeout(500, TimeUnit.MILLISECONDS)
            .except(Main::handleError);

        sleep(1000);

        // 这个 Promise 不会超时
        new Promise<>((res, rej) -> sleep(200, () -> res.accept("足够快")))
            .orTimeout(500, TimeUnit.MILLISECONDS)
            .then(result -> {
                System.out.println("  未超时的结果: " + result);
                return null;
            })
            .except(Main::handleError);
    }

    /**
     * 演示使用自定义 Executor。
     */
    private static void demoCustomExecutor() {
        System.out.println("\n--- 演示: 自定义 Executor ---");
        // 创建一个单线程的 Executor，所有回调将在这个线程上执行
        ExecutorService customExecutor = Executors.newSingleThreadExecutor(r -> {
            Thread t = new Thread(r, "自定义线程");
            System.out.println("  创建了: " + t.getName());
            return t;
        });

        Promise.resolved("初始值")
            .then(value -> {
                System.out.println("  第一步在线程: " + Thread.currentThread().getName());
                return value.toUpperCase();
            }, customExecutor) // 在自定义线程池中执行
            .then(value -> {
                System.out.println("  第二步在线程: " + Thread.currentThread().getName());
                return null;
            }) // 没有指定，应该回到默认线程池
            .lastly(customExecutor::shutdown); // 完成后关闭自定义线程池
    }

    /**
     * 所有演示的集中错误处理器。
     */
    private static void handleError(Throwable error) {
        System.err.println("\n--- 发生了一个错误 ---");
        if (error instanceof PromiseStepException) {
            PromiseStepException stepError = (PromiseStepException) error;
            System.err.println("  失败步骤: " + stepError.getStepName());
            System.err.println("  根本原因: " + stepError.getCause().getClass().getSimpleName());
            System.err.println("  原因消息: " + stepError.getCause().getMessage());
        } else {
            System.err.println("  发生未预期的错误: " + error.getClass().getSimpleName() + " - " + error.getMessage());
        }
    }

    /**
     * 阻塞当前线程指定的毫秒数。
     * @param millis 阻塞的毫秒数
     */
    private static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    /**
     * 阻塞当前线程，然后在指定的 Runnable 执行后继续。
     * @param millis  阻塞的毫秒数
     * @param andThen 阻塞后执行的任务
     */
    private static void sleep(long millis, Runnable andThen) {
        try {
            Thread.sleep(millis);
            andThen.run();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}
